package com.example.demo.config;

import com.example.demo.utils.JwtUtils;
import com.example.demo.common.R;
import com.example.demo.utils.WebUtils;
import lombok.extern.log4j.Log4j2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * security注解 默认不开启
 *  @EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled=true)开启注解
 *  @Secured({"ROLE_USER","ROLE_ADMIN"}) 多角色只能or关系, 只能用于角色不能用权限
 *  @PreAuthorize("hasRole('USER')") 用的多 可以配置多权限的and or关系 hasAnyRole, hasRole() AND hasAuthority()
 *  @PostAuthorize 用的不多
 */
@Log4j2
@EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled=true)
@EnableWebSecurity
@Configuration
public class SecurityConfig {
    @Autowired
    UserDetailsServiceImpl userDetailsServiceImpl;
    @Bean
    public PasswordEncoder passwordEncoder(){
        //return new BCryptPasswordEncoder(); //特点：同样的明文密码生成的加密密码字符串都不一样 安全性高
        //return new Md5PasswordEncoder();
        return NoOpPasswordEncoder.getInstance();
    }
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests()
                //.mvcMatchers("/user/**").hasRole("USER")
                .anyRequest().permitAll()

                //开启表单登录认证
                .and()
                .formLogin()
                //.loginProcessingUrl("/login")
                .successHandler((request, response, auth) -> {
                    log.info("登录成功");
                    LoginUser loginUser = (LoginUser)auth.getPrincipal();
                    String token = JwtUtils.createTokenFromUser(loginUser);

                    //允许浏览器响应跨域服务器返回的数据
                    //response.setHeader("Access-Control-Allow-Origin","*");
                    WebUtils.response(R.ok(token), response);
                })
                .failureHandler((request, response, e) -> {
                    String msg=null;
                    if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
                        msg="用户名或密码错误!";
                    } else if (e instanceof DisabledException) {
                        msg="账户被禁用!";
                    } else {
                        msg="登录失败!";
                    }
                    WebUtils.response(R.fail(msg), response);
                })
                .and()
                .userDetailsService(userDetailsServiceImpl)

                //授权异常处理
                .exceptionHandling()
                .authenticationEntryPoint((request, response, e) -> {
                    WebUtils.response(R.fail("未登录"), response);
                })
                .accessDeniedHandler((request, response, e) -> {
                    WebUtils.response(R.fail("没权限"), response);
                })

                //开启jwt关闭session
                .and()
                .addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                .sessionManagement().sessionCreationPolicy((SessionCreationPolicy.STATELESS))

                // 关闭csrf
                .and()
                .csrf().disable();

        return http.build();
    }
}